package syntax;

import java.io.OutputStream;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.NavigableMap;
import java.util.Set;
import java.util.Stack;
import java.util.TreeMap;
import parameters.Parameters;
import semantics.State;
import test.MinDelayTestStrategy;
import test.TestStrategy;
import util.TaggedWeightedObject;
import util.WeightedObject;
//import com.modeliosoft.modelio.javadesigner.annotations.objid;

//@objid ("0033fd9c-e708-10da-ac48-082e5f2773a2")
public class TIOA extends NamedObject {
    //@objid ("0007d906-e72b-10da-ac48-082e5f2773a2")

    private Location initialLocation;
    private Repository repository;
//    Stack<State> states;
//    Set<State> handledStates;
    //State currentState;

    public TIOA(String name) {
        super(name);
        repository = Repository.getInstance();
        initialLocation = null;
//        states = new Stack<State>();
//        handledStates = new HashSet<State>();
    }

    public TIOA(String name, Location initialLocation) {
        super(name);
        repository = Repository.getInstance();
        this.initialLocation = initialLocation;
    }

    @Override
    public Object clone() {
        Repository repositoryClone = this.repository.clone();
        NamedObject namedObject = (NamedObject)super.clone();
        TIOA tioaClone = new TIOA(namedObject.getName());
        tioaClone.setRepository(repositoryClone);
        Location loc = repositoryClone.getLocation(this.getInitialLocation().getName());
        tioaClone.setInitialLocation(loc);
        return tioaClone; //To change body of generated methods, choose Tools | Templates.
    }
    
    

    public Location getInitialLocation() {
        return initialLocation;
    }

    public void setInitialLocation(Location initialLocation) {
        this.initialLocation = initialLocation;
    }

//    public void addTransition(Transition tr) {
//        Location src, dest;
//        transitions.add(tr);
//
//        //adding source location: if does not exist in the collection
//        src = tr.getSrcLocation();
//        locations.put(src.getName(), src);
//
//        if (!locations.containsKey(src.getName())) {
//            locations.put(src.getName(), src);
//        }
//        src.getOutgoingTrans().add(tr);
//        //adding destination location: if does not exist in the collection
//        dest = tr.getDestLocation();
//        if (!locations.containsKey(dest.getName())) {
//            locations.put(dest.getName(), dest);
//        }
//        dest.getIngoingTrans().add(tr);
//
//        //adding 
//    }
//    public Float minDelay(Transition transition) {
//        float res = 0F;
//        float v_x;
//        float delay = 0F;
//
//        ClockGuard guard = transition.getClockGuard();
//        if (guard == null) {
//            return 0F;
//        }
//        for (ClockConditionAtom atom : guard.getConditionAtom()) {
//            v_x = currentState.getValuation().get(atom.getClock());
//            if (atom.getLowerRelation() == Operator.Op.getLT()) {
//                delay = atom.getDomain().getL_b() - v_x + Parameters.getEpsilon();
//            } else {
//                delay = atom.getDomain().getL_b() - v_x;
//            }
//            if (delay > res) {
//                res = delay;
//            }
//        }
//
//        return res;
//    }

//    private State initializeState() {
//        State initialState = new State();
//        initialState.setLocation(initialLocation);
//        initialLocation.setReachable(true);
//        for (Clock clock : repository.getAllClocks()) {
//            initialState.getValuation().put(clock, 0F);
//        }
//        return initialState;
//    }

//    private State computeNewStateWithoutReset(State currState, Transition transition, Float delay) {
//        State state = new State();
//        state.setLocation(transition.getDestLocation());
//        for (Clock clock : repository.getAllClocks()) {
//            state.getValuation().put(clock, Math.min(currState.getValuation().get(clock) + delay, Parameters.getInfinity()));
//        }
//        return state;
//    }
//
//    private State applyReset(State state, Transition transition) {
//        for (Clock clock : transition.getResetClocks()) {
//            state.getValuation().put(clock, 0F);
//        }
//        return state;
//    }

    public void testGeneration() {
        TestStrategy strategy = new MinDelayTestStrategy();
        testGeneration(strategy);
    }

    public void testGeneration(TestStrategy strategy) {
        System.out.println(strategy.test(this));
        //Extract preambule
        //generate test
        //extract postambule
    }

//    public void reachabilityAnalysis() {
//        Float timeStep = 0F;
//        Float minDelay = 0F, maxDelay = 0.F;
//        State newState;
//        State currentState;
//        //Initialise initial state:
//        //current state = l0 && v_x <- 0 for each x
//        states = new Stack<State>();
//        handledStates = new HashSet<State>();
//        currentState = initializeState();
//        states.push(currentState);
//
//        while (!states.empty()) {
//            currentState = states.pop();
//            handledStates.add(currentState);
//            //float outputDelay = Parameters.getInfinity();
//            List<Transition> unreacheableOutputTrans = new LinkedList<Transition>();
//            List<Transition> potTrans = new LinkedList<Transition>();
//            Domain bestExecRange = Domain.getInfinityUnreachableDomain();
//            Domain currRange;
//
//            //TreeMap<Domain, Transition> transTree = new TreeMap<>(domainComparator);
//            List<TaggedWeightedObject> minTransitions = new LinkedList<TaggedWeightedObject>();
//            List<TaggedWeightedObject> maxTransitions = new LinkedList<TaggedWeightedObject>();
//            TaggedWeightedObject wo;
//            for (Transition transition : currentState.getLocation().getOutgoingTrans()) {
//                if (transition.getMessage().isOutput()) {
//                    minDelay = currentState.minDelay(transition);
//                    maxDelay = currentState.maxDelay(transition);
//                    wo = new TaggedWeightedObject(TaggedWeightedObject.TAG.MIN, minDelay, transition);
//                    minTransitions.add(wo);
//                    wo = new TaggedWeightedObject(TaggedWeightedObject.TAG.MAX, maxDelay, transition);
//                    maxTransitions.add(wo);
//                }
//                else {
//                    potTrans.add(transition);
//                }                                                
//            }
//            List<TaggedWeightedObject> toHandleTrans = new LinkedList<TaggedWeightedObject>();
//            toHandleTrans.addAll(minTransitions);
//            toHandleTrans.addAll(maxTransitions);
//            
//            Collections.sort(toHandleTrans);
//            Iterator<TaggedWeightedObject> iter = toHandleTrans.listIterator();
//            while (iter.hasNext()) {
//                wo = iter.next();
//                if (wo.getTag() == TaggedWeightedObject.TAG.MAX) {
//                    break;
//                } else {
//                    potTrans.add((Transition)wo.getObject());
//                }                
//            }
//            while (iter.hasNext()) {
//                wo = iter.next();
//                if (wo.getTag() == TaggedWeightedObject.TAG.MIN) {
//                    unreacheableOutputTrans.add((Transition)wo.getObject());
//                }               
//            }
//                        
//            for (Transition transition : unreacheableOutputTrans) {
//                transition.setReachable(false);
//            }
//
//            for (Transition transition : potTrans) {                
//                if (currentState.isExecutable(transition)) {
//                    transition.setReachable(true);
//                    transition.addLastStateToPool(currentState);
//                    newState = currentState.computeNewState(transition);
//                    if (!handledStates.contains(newState)) {
//                        transition.getDestLocation().setReachable(true);
//                        states.push(newState);
//                    }
//                }
////                timeStep = currentState.minDelay(transition);
////                //if v+delta \models G time guard 
////                newState = computeNewStateWithoutReset(currentState, transition, timeStep);
////                if (newState.isModel(transition.getClockGuard())) {
////                    transition.setReachable(true);
////                    applyReset(newState, transition);
////                    if (!handledStates.contains(newState)) {
////                        transition.getDestLocation().setReachable(true);
////                        states.push(newState);
////                    }
////                }
//            }
//
//        }
//    }
    
    public void computeShortestPaths() {
    
    }
    

    public String printTIOA() {

        StringBuilder out = new StringBuilder();

        out.append("Name: ").append(getName()).append("\n");
        out.append("=====================================" + "\n");
        out.append("Initial location: ").append(initialLocation).append("\n");
        out.append("=====================================" + "\n");
        out.append("Clocks: " + "\n");
        out.append(repository.printClocks());
        out.append("=====================================\n");
        out.append("Messages: \n");
        out.append(repository.printMessages());
        out.append("=====================================\n");
        out.append("Locations: \n");
        out.append(repository.printLocations());
        out.append("=====================================\n");
        out.append("Transitions: \n");
        out.append(repository.printTransitions());
        out.append("=====================================\n");
        //reachabilityAnalysis();
        for (Location location : repository.getAllLocations()) {
            out.append(location.toString()).append(" ").append(location.isReachable()).append("\n");
        }
        for (Transition transition : repository.getAllTransitions()) {
            out.append(transition.toString()).append(" ").append(transition.isReachable()).append("\n");
        }
        return out.toString();
    }

    public Repository getRepository() {
        return repository;
    }

    public void setRepository(Repository repository) {
        this.repository = repository;
    }
    
    public Transition getFictiousTransition() {
        Transition res = null;
        res = new Transition(null, initialLocation, initialLocation, repository.getInternalAction(), null);
//        res = new Transition(initialLocation, initialLocation, repository.getInternalAction(), null, null, true, null);
        return res;
    }
}
